<template>
  <div class="editor-container">
    <el-select v-model="language" @change="changeLanguage" class="language-select">
      <el-option v-for="option in languages" :key="option" :label="option" :value="option"/>
    </el-select>
    <div id="codeEditBox"></div>
  </div>
</template>
<script setup lang="ts">
import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker";
import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker";
import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker";
import tsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker";
import EditorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";
import * as monaco from "monaco-editor";
import {nextTick, ref, onBeforeUnmount, onMounted, watch} from "vue";
import {useRoute} from "vue-router";


const text = ref("");
const route = useRoute();
const language = ref("vue");
const msg = ref();
const loading = ref(false);

const languages = ref([
  "javascript",
  "typescript",
  "html",
  "css",
  "json",
  "vue"
]);

//
// MonacoEditor start
//
onBeforeUnmount(() => {
  editor.dispose();
});
// @ts-ignore
self.MonacoEnvironment = {
  getWorker(_: string, label: string) {
    if (label === "json") {
      return new jsonWorker();
    }
    if (label === "css" || label === "scss" || label === "less") {
      return new cssWorker();
    }
    if (label === "html" || label === "handlebars" || label === "razor") {
      return new htmlWorker();
    }
    if (["typescript", "javascript"].includes(label)) {
      return new tsWorker();
    }
    return new EditorWorker();
  }
};
let editor: monaco.editor.IStandaloneCodeEditor;

const editorInit = () => {
  nextTick(() => {
    monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
      noSemanticValidation: true,
      noSyntaxValidation: false
    });
    monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
      target: monaco.languages.typescript.ScriptTarget.ES2016,
      allowNonTsExtensions: true
    });

    !editor ? editor = monaco.editor.create(document.getElementById("codeEditBox") as HTMLElement, {
          value: text.value, // 编辑器初始显示文字
          language: "vue", // 语言支持自行查阅demo
          automaticLayout: true, // 自适应布局
          theme: "vs-dark", // 官方自带三种主题vs, hc-black, or vs-dark
          minimap: {
            enabled: true
          },
          tabSize: 2,
          fontSize: 14,
          lineNumbers: "on",
          roundedSelection: true,
          scrollBeyondLastLine: false,
          readOnly: false,
          contextmenu: true,
          // 启用代码提示
          suggest: {
            preview: true,
            showMethods: true,
            showFunctions: true,
            showConstructors: true,
            showFields: true,
            showVariables: true,
            showClasses: true,
            showStructs: true,
            showInterfaces: true,
            showModules: true,
            showProperties: true,
            showEvents: true,
            showOperators: true,
            showUnits: true,
            showValues: true,
            showConstants: true,
            showEnums: true,
            showEnumMembers: true,
            showKeywords: true,
            showWords: true,
            showColors: true,
            showFiles: true,
            showReferences: true,
            showFolders: true,
            showTypeParameters: true,
            showSnippets: true,
            showUsers: true,
            showIssues: true
          }
        }) :
        editor.setValue("");
    // console.log(editor)
    // 监听值的变化
    editor.onDidChangeModelContent((val: any) => {
      text.value = editor.getValue();

    });
  });
};

// 注册 Vue 语言支持
const registerVueLanguage = () => {
  monaco.languages.register({ id: 'vue' })

  monaco.languages.setMonarchTokensProvider('vue', {
    defaultToken: '',
    tokenPostfix: '.vue',

    keywords: [
      'template', 'script', 'style', 'setup', 'export', 'default', 'import',
      'from', 'as', 'const', 'let', 'var', 'function', 'return', 'if', 'else',
      'for', 'while', 'switch', 'case', 'break', 'continue', 'try', 'catch',
      'finally', 'throw', 'new', 'this', 'super', 'extends', 'implements',
      'interface', 'type', 'namespace', 'module', 'declare', 'public',
      'private', 'protected', 'static', 'readonly', 'abstract', 'async',
      'await', 'of', 'in', 'instanceof', 'typeof', 'void', 'null', 'undefined',
      'true', 'false', 'any', 'unknown', 'never', 'object', 'string', 'number',
      'boolean', 'symbol', 'bigint', 'void', 'null', 'undefined', 'true',
      'false', 'any', 'unknown', 'never', 'object', 'string', 'number',
      'boolean', 'symbol', 'bigint'
    ],

    typeKeywords: [
      'string', 'number', 'boolean', 'any', 'void', 'null', 'undefined',
      'never', 'object', 'unknown', 'symbol', 'bigint'
    ],

    operators: [
      '=', '>', '<', '!', '~', '?', ':', '==', '<=', '>=', '!=',
      '&&', '||', '++', '--', '+', '-', '*', '/', '&', '|', '^', '%',
      '<<', '>>', '>>>', '+=', '-=', '*=', '/=', '&=', '|=', '^=',
      '%=', '<<=', '>>=', '>>>='
    ],

    symbols: /[=><!~?:&|+\-*\/\^%]+/,
    escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,

    tokenizer: {
      root: [
        [/<template>/, { token: 'keyword.template', next: '@template' }],
        [/<script>/, { token: 'keyword.script', next: '@script' }],
        [/<style>/, { token: 'keyword.style', next: '@style' }],
        [/[{}()\[\]]/, '@brackets'],
        [/[a-zA-Z_$][\w$]*/, {
          cases: {
            '@typeKeywords': 'type',
            '@keywords': 'keyword',
            '@default': 'identifier'
          }
        }],
        [/[0-9_]*\.[0-9_]+([eE][\-+]?\d+)?[fFdD]?/, 'number.float'],
        [/0[xX][0-9a-fA-F_]+/, 'number.hex'],
        [/0[0-7_]+/, 'number.octal'],
        [/0[bB][01_]+/, 'number.binary'],
        [/[0-9_]+/, 'number'],
        [/[;,.]/, 'delimiter'],
        [/"([^"\\]|\\.)*$/, 'string.invalid'],
        [/'([^'\\]|\\.)*$/, 'string.invalid'],
        [/"/, 'string', '@string_double'],
        [/'/, 'string', '@string_single'],
        [/`/, 'string', '@string_backtick'],
        [/@symbols/, {
          cases: {
            '@operators': 'operator',
            '@default': ''
          }
        }],
        [/[ \t\r\n]+/, 'white'],
        [/\/\*/, 'comment', '@comment'],
        [/\/\/.*$/, 'comment']
      ],

      template: [
        [/<\/template>/, { token: 'keyword.template', next: '@pop' }],
        [/./, 'template.content']
      ],

      script: [
        [/<\/script>/, { token: 'keyword.script', next: '@pop' }],
        [/./, 'script.content']
      ],

      style: [
        [/<\/style>/, { token: 'keyword.style', next: '@pop' }],
        [/./, 'style.content']
      ],

      comment: [
        [/[^\/*]+/, 'comment'],
        [/\/\*/, 'comment', '@push'],
        ["\\*/", 'comment', '@pop'],
        [/[\/*]/, 'comment']
      ],

      string_double: [
        [/[^\\"]+/, 'string'],
        [/@escapes/, 'string.escape'],
        [/\\./, 'string.escape.invalid'],
        [/"/, 'string', '@pop']
      ],

      string_single: [
        [/[^\\']+/, 'string'],
        [/@escapes/, 'string.escape'],
        [/\\./, 'string.escape.invalid'],
        [/'/, 'string', '@pop']
      ],

      string_backtick: [
        [/[^\\`]+/, 'string'],
        [/@escapes/, 'string.escape'],
        [/\\./, 'string.escape.invalid'],
        [/`/, 'string', '@pop']
      ]
    }
  })
}

onMounted(async () => {
  registerVueLanguage()
  editorInit()
})

onBeforeUnmount(() => {
  if (editor) {
    editor.dispose()
  }
})

watch(() => language, (newLang) => {
  language.value = newLang
  if (editor) {
    const model = editor.getModel()
    monaco.editor.setModelLanguage(model, newLang)
  }
})

const changeLanguage = () => {
  monaco.editor.setModelLanguage(editor.getModel(), language.value);


  //  editor.updateOptions({
  //           language: "objective-c"
  //       });
};
</script>

<style scoped lang="scss">
.editor-container {
  width: 100%;
  height: 80%;
  padding: 15px;
  background: #ffffff;
}

.language-select {
  margin-bottom: 10px;
  width: 200px;
}

#codeEditBox {
  width: 100%;
  height: 100%;
}
</style>
